#include <d3dx9.h>
#include <ctime>
#include <string>

#include "demo_base.h"

#pragma comment(lib,"d3d9.lib") 
#pragma comment(lib,"d3dx9.lib")

//-------------------------------------------------------------------------------------------------------------------//
timer::timer()
{
	::QueryPerformanceFrequency(&m_freq);
}

double timer::get_time()
{
	LARGE_INTEGER tick = {0, 0};
	::QueryPerformanceCounter(&tick);
	return (double)(tick.QuadPart) / (double)(m_freq.QuadPart);
}


//-------------------------------------------------------------------------------------------------------------------//
demo_base& demo_base::get_instance()
{
	static demo_base inst;
	return inst;
}

bool demo_base::init(HINSTANCE instance, const char *title, int width, int height, D3DDEVTYPE device_type)
{
	m_d3d = NULL;
	m_device = NULL;
	m_font = NULL;
	m_frame_count = 0;
	m_last_time = 0.0;
	m_wnd = NULL;

	memset(m_fps_string, 0, sizeof(m_fps_string));

	if(!init_wnd(instance, title, width, height))
	{
		return false;
	}
	if(!init_d3d(device_type))
	{
		return false;
	}
	return true;
}

void demo_base::release()
{
	cleanup();
}

bool demo_base::init_wnd(HINSTANCE instance, const char *title, int width, int height)
{
  	const char class_name[] = "demo_base";

    // Register the window class
    WNDCLASSEX wc =
    {
        sizeof(WNDCLASSEX), CS_CLASSDC, m_msg_proc, 0L, 0L,
        GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
        class_name, NULL
    };

    RegisterClassEx(&wc);

    // Create the application's window
    m_wnd = CreateWindow(class_name, title,
                         WS_OVERLAPPEDWINDOW, 0, 0, width, height,
                         NULL, NULL, instance, NULL);
	return (m_wnd != NULL);

}
	
bool demo_base::init_d3d(D3DDEVTYPE device_type)
{
    // Create the D3D object.
    if(NULL == (m_d3d = Direct3DCreate9(D3D_SDK_VERSION)))
        return false;

    // Set up the structure used to create the D3DDevice
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;

    // Create the D3DDevice
    if(FAILED(m_d3d->CreateDevice(D3DADAPTER_DEFAULT, device_type, m_wnd,
                                  D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                  &d3dpp, &m_device)))
    {
        return false;
    }

	// Create Font
	D3DXFONT_DESC font_desc;
    ZeroMemory(&font_desc, sizeof(font_desc));

    font_desc.Height            = 25;        // in logical units
    font_desc.Width             = 12;        // in logical units
    font_desc.Weight            = 500;       // boldness, range 0(light) - 1000(bold)
    font_desc.Italic            = FALSE;
    font_desc.CharSet           = DEFAULT_CHARSET;
    font_desc.OutputPrecision   = 0;
    font_desc.Quality           = 0;
    font_desc.PitchAndFamily    = 0;

	::strcpy(font_desc.FaceName, "Times New Roman");

	if(FAILED(::D3DXCreateFontIndirect(m_device, &font_desc, &m_font)))
    {
         return false;
    }

    return true;
}

void demo_base::cleanup()
{
	m_d3d->Release();
	m_device->Release();
	m_font->Release();

}

void demo_base::update_fps()
{
	++m_frame_count;
	double cur_time = m_timer.get_time();
	if (cur_time - m_last_time > 1.0)
	{
		float fps = (float)(m_frame_count / (cur_time - m_last_time));
		unsigned short str_len = sprintf(m_fps_string, "FPS:%0.2f", fps);
		m_fps_string[str_len] = '\0';
		m_last_time = cur_time;
		m_frame_count = 0;
	}
}

void demo_base::display()
{
	// Clear the backbuffer to a black color
    m_device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    // Begin the scene
    if (SUCCEEDED(m_device->BeginScene()))
    {
		// draw scene
		m_render();

		// draw fps 
		update_fps();
		RECT rect = {0, 0, 108, 20};
		m_font->DrawText(NULL, m_fps_string, -1, &rect, DT_NOCLIP, 0xFFFF0000);
		
        // End the scene
        m_device->EndScene();
    }

    // Present the backbuffer contents to the display
    m_device->Present(NULL, NULL, NULL, NULL);
}

void demo_base::main_loop()
{
    // Show the window
    ShowWindow(m_wnd, SW_SHOWDEFAULT);
    UpdateWindow(m_wnd);

    // Enter the message loop
    MSG msg;
    ZeroMemory(&msg, sizeof(msg));
    while(msg.message != WM_QUIT)
    {
        if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
		{	
			display();
		}
    }

}

void demo_base::register_render(func_render render)
{
	m_render = render;

}

void demo_base::register_msg_proc(func_msg_proc msg_proc)
{
	m_msg_proc = msg_proc;

}

IDirect3DDevice9* demo_base::get_device()
{
	return m_device;

}

double demo_base::get_time()
{
	return m_timer.get_time();
}
